package org.offsync.nio

//import scala.collection.mutable.Array
import scala.collection.mutable.ArrayBuffer
import java.nio.channels.{Selector => JavaSelector}
import java.nio.channels.{SelectionKey => JavaSelectionKey}
import java.nio.channels.CancelledKeyException
import java.nio.channels.ClosedChannelException
import org.offsync.util.Trace.trace
import java.util.concurrent.atomic.AtomicBoolean

trait InterestParty {
  class InterestOperation(
      f: Option[Throwable] => Unit,
      val selector: Selector,
      var key: JavaSelectionKey) extends (Option[Throwable] => Unit) {
    def apply(result: Option[Throwable]) = {
      f(result)
    }
  }
  
  private var cancelAcceptKey: () => Unit = { () => }
  private var cancelConnectKey: () => Unit = { () => }
  private var cancelReadKey: () => Unit = { () => }
  private var cancelWriteKey: () => Unit = { () => }
  
  val ops = new Array[InterestOperation](32)
  
  private def forEachMaskIndex(interestMask: Int)(f: Int => Unit): Unit = {
    (0 to ops.length - 1).foreach { interestIndex =>
      val mask = 1 << interestIndex
      if ((mask & interestMask) != 0) {
        f(interestIndex)
      }
    }
  }
  
  def onceOnly(f: Option[Throwable] => Unit) = new (Option[Throwable] => Unit) {
    val completed = new AtomicBoolean(false)
    
    def apply(result: Option[Throwable]): Unit = {
      if (!completed.getAndSet(true)) {
        f(result)
      }
    }
  }
  
  def onCanAccept(ioService: IoService)(f: Option[Throwable] => Unit) = {
    ioService.acceptSelector.execute {
      val onceOnlyF = onceOnly(f)
      val key = register(ioService.acceptSelector, onceOnlyF)
      cancelAcceptKey = { () =>
        ioService.acceptSelector.execute {
          key.cancel()
          onceOnlyF(Some(new CancelledKeyException()))
        }
      }
    }
  }
  
  def onCanConnect(ioService: IoService)(f: Option[Throwable] => Unit) = {
    ioService.connectSelector.execute {
      val key = register(ioService.connectSelector, f)
      cancelConnectKey = { () =>
        ioService.connectSelector.execute {
          key.cancel()
          f(Some(new CancelledKeyException()))
        }
      }
    }
  }
  
  def onCanRead(ioService: IoService)(f: Option[Throwable] => Unit) = {
    ioService.readSelector.execute {
      val key = register(ioService.readSelector, f)
      cancelReadKey = { () =>
        ioService.readSelector.execute {
          key.cancel()
          f(Some(new CancelledKeyException()))
        }
      }
    }
  }
  
  def onCanWrite(ioService: IoService)(f: Option[Throwable] => Unit) = {
    ioService.writeSelector.execute {
      val key = register(ioService.writeSelector, f)
      cancelWriteKey = { () =>
        ioService.writeSelector.execute {
          key.cancel()
          f(Some(new CancelledKeyException()))
        }
      }
    }
  }
  
  protected def cancelKeys(): Unit = {
    cancelAcceptKey()
    cancelConnectKey()
    cancelReadKey()
    cancelWriteKey()
  }
  
  protected def register(selector: Selector, f: Option[Throwable] => Unit): JavaSelectionKey
  
  def process(selector: Selector, selectionKey: JavaSelectionKey): Unit = {
    val f = selectionKey.attachment.asInstanceOf[Option[Throwable] => Unit]
    try {
      var readyMask = selectionKey.readyOps()
      
      assert(selector.op == readyMask)
      selectionKey.cancel()
      f(None)
    } catch {
      case e => {
        selectionKey.cancel()
        
        f(Some(e))
      }
    }
  } 
}

